import Sortablejs from 'sortablejs'
import { isFunction } from 'lodash-es'

import type { SortableEvent, MoveEvent } from 'sortablejs'
import type { BwDragModel, BwSortableModel } from './model'

export function bwSortableUtil(ele: HTMLElement, config: BwDragModel) {
  /** 生成拖拽经过标识DIV */
  const createHoverEle = () => {
    const divHover = document.createElement('div')
    divHover.style.display = 'block'
    divHover.style.height = '4px'
    divHover.style.width = '100%'
    divHover.style.backgroundColor = '#0089ff'
    divHover.style.position = 'absolute'
    divHover.style.left = '0px'
    divHover.style.top = 'auto'
    divHover.style.bottom = '-1px'

    return divHover
  }

  /** 设置拖拽经过标识DIV */
  const setHoverPosition = (
    ele: HTMLElement,
    toEle: HTMLElement,
    divHover: HTMLDivElement,
    clientY: number
  ) => {
    let position: 'top' | 'bottom' = 'bottom'
    /** 判断是否是容器元素 */
    if (ele === toEle) {
      divHover.remove()
      ele.appendChild(divHover)
    } else {
      const heightMiddle = (ele.offsetHeight ?? 0) / 2
      const eleClientY = ele.getBoundingClientRect().top

      if (clientY > eleClientY + heightMiddle) {
        position = 'bottom'
        divHover.style.top = 'auto'
        divHover.style.bottom = '-1px'
      } else {
        position = 'top'
        divHover.style.top = '-1px'
        divHover.style.bottom = 'auto'
      }

      divHover.remove()
      ele.appendChild(divHover)
    }
    return position
  }

  const init = (ele: HTMLElement, options?: Record<string, any>) => {
    const divHover = createHoverEle()
    const curDragInfo: BwSortableModel = {}
    const sortable = Sortablejs.create(ele, {
      group: config.group,
      filter: config.filter,
      sort: config.sort ?? true,
      animation: config.animation ?? 80,
      draggable: config.draggable ?? '.bw-design-drag-item',
      chosenClass: config.chosenClass,

      onChoose: (evt: SortableEvent) => {
        if (isFunction(config.onChoose)) {
          config.onChoose(evt)
        }
      },
      onUnchoose: (evt: SortableEvent) => {
        if (isFunction(config.onUnchoose)) {
          config.onUnchoose(evt)
        }
      },

      onStart: (evt: SortableEvent) => {
        if (isFunction(config.onStart)) {
          config.onStart(evt)
        }
      },

      onUpdate: (evt: SortableEvent) => {
        if (isFunction(config.onUpdate)) {
          config.onUpdate(evt)
        }
      },

      onChange: (evt: SortableEvent) => {
        if (isFunction(config.onChange)) {
          config.onChange(evt)
        }
      },

      onAdd: (evt: SortableEvent) => {
        if (isFunction(config.onAdd)) {
          config.onAdd(evt)
        }
      },

      onMove: (evt: MoveEvent, originalEvent: Event) => {
        // @ts-ignore
        const { clientX, clientY } = originalEvent ?? {}
        const { clientX: X, clientY: Y } = curDragInfo?.originalEvent ?? {}

        if (clientX && clientY && clientX !== X && clientY !== Y) {
          curDragInfo.to = evt.to
          curDragInfo.from = evt.from
          curDragInfo.related = evt.related
          curDragInfo.dragged = evt.dragged
          curDragInfo.originalEvent = originalEvent

          if (config.isHoverBubble) {
            curDragInfo.position = setHoverPosition(
              evt.related,
              evt.to,
              divHover,
              clientY
            )
          }
        }
        return false
      },
      onEnd: (evt: SortableEvent) => {
        if (config.isHoverBubble) {
          divHover.remove()
        }

        if (isFunction(config.onEnd)) {
          config.onEnd(evt)
        } else if (isFunction(config.onDragEnd)) {
          evt.stopPropagation()
          config.onDragEnd(curDragInfo)
        }
      },

      ...(options ?? {}),
    })
    return sortable
  }

  if (config.receivClass) {
    const eles = ele.getElementsByClassName(config.receivClass)
    if (eles.length) {
      for (let i = 0; i < eles.length; i++) {
        const curEle = eles[i] as HTMLElement

        if (curEle) {
          const isDrag = curEle.getAttribute('isDrag')
          if (!isDrag) {
            curEle.setAttribute('isDrag', 'true')
            init(curEle)
          }
        }
      }
    }
  }
}
